#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "CheapText.h"

#include "Class.h"
#include "RegularExp.h"
#include "Port.h"
#include "Error.h"
#include "Math.h"
#include "String.h"
#include "TextFormatter.h"

//---- CheapText ---------------------------------------------------------------

NewMetaImpl(CheapText,Text, (T(capacity), TV(cont,size)));

CheapText::CheapText(int s, Font *fd)
{
	Init(s, fd, 0, 0);
}

CheapText::CheapText(const byte *t, int len, Font *fd)
{
	Init(0, fd, t, len);
}

CheapText::CheapText(const char *t, int len, Font *fd)
{
	Init(0, fd, (const byte*)t, len);
}

void CheapText::Init(int cap, Font *fd, const byte *t, int l)
{
	if (l == -1)
		l= strlen((char*)t);
	if (cap == 0)
		cap= (l/5 + 1) * 6;
	capacity= Math::Max(cTextInitCap, cap);
	cont= new byte[capacity];
	if (l > 0 && t)
		MemCpy(cont, t, l);
	size= l;
	SetFont(fd);
	SetDefTab(fd->Width(' ')*8);
	Terminate();
}

void CheapText::InitNew()
{
	Text::InitNew();
	Init(0, gSysFont);
}

CheapText::~CheapText()
{
	SafeDelete(cont);
}

void CheapText::Terminate()
{
	if (IsTerminated())
		return;
	byte null= '\0';
	InsertBytes(&null, 1, Size(), Size());
}

void CheapText::ReplaceRange(int from, int to, Text *src, int sfrom, int sto)
{
	CheapText *ct;
	byte *buf= 0;

	if (!CheckRange(End(), from, to))
		Error("PasteText", "out of range");

	if (!src->IsKindOf(CheapText)) { // convert
		int s= sto-sfrom;
		buf= new byte[s+1];
		src->CopyInStr(buf, s+1, sfrom, sto);
		sto= sto-sfrom;
		sfrom= 0;
		ct= new CheapText(buf, s);
	} else
		ct= (CheapText*)src;

	InsertBytes(ct->cont+sfrom, sto-sfrom, from, to);

	if (buf) {
		delete buf;
		delete ct;
	}
}

Text *CheapText::MakeScratchText(byte *buf, int len)
{
	if (buf)
		return new CheapText(buf, len);
	return new CheapText(len);
}

void CheapText::CopyInStr(byte *str, int sz, int from, int to)
{
	if (!CheckRange(size, from, to) || str == 0)
		return;

	register int l= Math::Min(to, from+sz-1) - from;
	MemCpy(str, &cont[from], l);
	str[l]= '\0';
}

void CheapText::AddChar(int at, byte b)
{
	InsertBytes(&b, 1, at, at);
}

byte *CheapText::GetLineAccess(byte**, int from, int to)
{
	if (!CheckRange(size, from, to))
		return 0;
	return &cont[from];
}

int CheapText::Search(RegularExp *rex, int *nMatched, int start, int range,
												bool dir)
{
	if (dir == cSearchForward)
		return rex->SearchForward((char*)cont, nMatched, start, End(), range, 0);
	return rex->SearchBackward((char*)cont, nMatched, start, End(), range, 0);
}

void CheapText::InsertBytes(byte *c, int n, int from, int to)
{
	int shift= n - (to - from);

	if (HighWaterMark(shift))
		Expand(GrowSize(capacity+shift));

	if (shift < 0)
		MemCpy(&cont[to+shift], &cont[to], size-to);
	else if (shift > 0)
		MemCpy(&cont[from+shift], &cont[from], size-from);

	//---- insert pasted text
	MemCpy(&cont[from], c, n);
	size += shift;
	if (LowWaterMark())
		Expand(capacity/2);
}

byte& CheapText::operator[](int i)
{
	if (!CheckRange(size, i, i))
		i= capacity-1;
	return cont[i];
}

int CheapText::Size()
{
	return size;
}

void CheapText::Expand(int newSize)
{
	if (newSize < Size())  // texts never shrinks
		return;
	cont= (byte*) Storage::ReAlloc(cont, newSize);
	size= Math::Min(newSize, size);
	capacity= newSize;
}

TextIter *CheapText::MakeIterator(int from, int to, void *placement)
{
	return new(placement) CheapTextIter(this, from, to);
}

const char *CheapText::AsString()
{
	return (char*) cont;
}

OStream& CheapText::PrintOn(OStream &s)
{
	Text::PrintOn(s);
	return s.PrintString(cont, size);
}

IStream& CheapText::ReadFrom(IStream &s)
{
	SafeDelete(cont);
	Text::ReadFrom(s);
	s.ReadString(&cont, &capacity);
	size= capacity;
	return s;
}

OStream &CheapText::PrintOnAsPureText(OStream &s)
{
	s.write(cont, End());
	return s;
}

IStream &CheapText::ReadFromAsPureText(IStream &s, long sizeHint)
{
	char ch;

	if (sizeHint > 0) {
		int delta= (int)(sizeHint - capacity); // !!!
		if (delta > 0)
			Expand(delta + 50);
	}
	size= 0;
	while (s.get(ch)) {
		if (HighWaterMark(1))
			Expand(GrowSize(capacity+1));
		cont[size++]= ch;
	}
	if (!s.eof())
		Error("ReadFromAsPureText", "something strange happened");
	Terminate();
	return s;
}

//----- CheapTextIter ----------------------------------------------------------

CheapTextIter::CheapTextIter(Text *s, int from, int to) : TextIter(s, from, to)
{
	if (!s->IsKindOf(CheapText))
		Error("CheapTextIter::CheapTextIter", "CheapText expected");
	font= s->GetFont();
}

int CheapTextIter::operator()(int *w, LineDesc* ld)
{
	CheapText *ctp= (CheapText*)ct;
	if (ld)
		ld->FromFont(font);
	unget= ce;
	if (ce == upto)
		return cEOT;
	int ch= ctp->cont[ce++];
	if (w)
		*w= font->Width(ch);
	return ch;
}

int CheapTextIter::Token(int *w, LineDesc* ld)
{
	CheapText *ctp= (CheapText*)ct;
	unget= ce;
	*w= 0;
	if (ld)
		ld->FromFont(font);
	if (ce >= upto)
		return cEOT;

	register int ch= ctp->CharAt(ce);
	if (Iswordwrap(ch)) {
		*w= font->Width(ch);
		ce++;
		return (ch);
	}
	while (ce < upto && !Iswordwrap(ctp->CharAt(ce))) {
		ch= ctp->CharAt(ce++);
		*w+= font->Width(ch);
	}
	return (ch);
}

